{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Decorator Application (Decorator Class)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you recalls how we wrote a parameterized decorator, we had to write a decorator factory that took in the arguments for our decorator and then returned the decorator (which could reference the arguments as free variables).\n",
    "\n",
    "Very simply:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def my_dec(a, b):\n",
    "    def dec(fn):\n",
    "        def inner(*args, **kwargs):\n",
    "            print('decorated function called: a={0}, b={1}'.format(a, b))\n",
    "            return fn(*args, **kwargs)\n",
    "        return inner\n",
    "    return dec"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "@my_dec(10, 20)\n",
    "def my_func(s):\n",
    "    print('hello {0}'.format(s))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "decorated function called: a=10, b=20\n",
      "hello world\n"
     ]
    }
   ],
   "source": [
    "my_func('world')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, our decorator factory was passed some arguments, and returned a callable which took one single parameter, the function being decorated, but also had access to the arguments passed to the factory."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, recall that we can make our class instances callable, simply by implementing the `__call__` method.\n",
    "\n",
    "Here's a simple example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class MyClass:\n",
    "    def __init__(self, a, b):\n",
    "        self.a = a\n",
    "        self.b = b\n",
    "        \n",
    "    def __call__(self):\n",
    "        print('MyClass instance called: a={0}, b={1}'.format(self.a, self.b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "my_class = MyClass(10, 20)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MyClass instance called: a=10, b=20\n"
     ]
    }
   ],
   "source": [
    "my_class()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So let's modify this just a bit, and have the `__call__` method be our decorator!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class MyClass:\n",
    "    def __init__(self, a, b):\n",
    "        self.a = a\n",
    "        self.b = b\n",
    "        \n",
    "    def __call__(self, fn):\n",
    "        def inner(*args, **kwargs):\n",
    "            print('MyClass instance called: a={0}, b={1}'.format(self.a, self.b))\n",
    "            return fn(*args, **kwargs)\n",
    "        return inner"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, we can decorate our functions this way:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "@MyClass(10, 20)\n",
    "def my_func(s):\n",
    "    print('Hello {0}!'.format(s))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Remember that `@MyClass(10, 20)` returned an object of type `MyClass`. But  that object is itself callable, so we could do something like:\n",
    "\n",
    "``\n",
    "my_func = MyClass(10, 20)(my_func)\n",
    "``\n",
    "\n",
    "or, more simply\n",
    "\n",
    "``\n",
    "@MyClass(10, 20)\n",
    "def my_func(s):\n",
    "    print(s)\n",
    "``"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MyClass instance called: a=10, b=20\n",
      "Hello Python!\n"
     ]
    }
   ],
   "source": [
    "my_func('Python')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So as you can see, we can also use callable classes to decorate functions!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
